#include"game.h"

/**
 * @brief 解码并处理接收数据
 * 
 * @param clientNode 用户TCP链表结点
 * @param clienData 接收到的数据
 */
void client_data_recv(Node_t* clientNode,tcp_client_data clienData)
{
    if(gameStart == 0) //如果游戏未开始，遍历客户端数据链表  //请问如何遍历客户端链表？？？客户端链表是什么？？怎么定义的？？
    {
        if(clienData.command==START_GAME) //判断该客户端发送是否为开始游戏指令
        {
            clientNode->data.start_game = 1; //该客户端游戏开始标志置位
            listSnake[clientNode->data.sign]->head->data.start_game = 1;//listSnake在哪里定义的？？？是什么意思？？？
        }
        Node_t* nowNode = listAddrHead->head->next;//listAddrHead在哪里定义的？？？
        if(listAddrHead->head->next==NULL)
        {
            return;
        }
        while (1)
        {
            if(nowNode->data.start_game==0) //判断是否有用户尚未准备开始游戏
            {
                break;
            }
            if(nowNode->next==NULL)
            {
                gameStart = 1;  //游戏开始标志置位
                break;
            }
            nowNode = nowNode->next;
        }
    }
	else//gameStart != 0 表示游戏已经开始了
	{
        int sign = clientNode->data.sign;
    
        if(clienData.command==EXIT_GAME) //先判断该用户是否退出游戏
        {
            clientNode->data.start_game = 0;
        }else if(clienData.direc==UP && (listSnake[sign]->head->data.direc!=UP && listSnake[sign]->head->data.direc!= DOWN)){  //判断玩家是否将蛇头方向置为向上
            //上面的判断是什么意思？？？应该是只有不在下的时候，才会响应上。
			//1. clienData.direc==UP
			//2. listSnake[sign]->head->data.direc!=UP
			//3. listSnake[sign]->head->data.direc!= DOWN
			if((listSnake[sign]->head->data.direc==LEFT || listSnake[sign]->head->data.direc==RIGHT) && listSnake[sign]->head->next->data.direc==DOWN)
            {
				//上面的判断是什么意思？？？
				//1. listSnake[sign]->head->data.direc==LEFT
				//2. listSnake[sign]->head->data.direc==RIGHT
				//3. listSnake[sign]->head->next->data.direc==DOWN 
            }else{
                pthread_rwlock_wrlock(&snakeRwlock);//没见过这个函数pthread_rwlock_wrlock
                listSnake[sign]->head->data.direc = UP;//这里应该是核心代码！！！！！！！！！！！！！！！！
                pthread_rwlock_unlock(&snakeRwlock);//没见过这个函数pthread_rwlock_unlock
                printf("用户:%d 向上\n",sign);
            }
        }else if(clienData.direc==DOWN && (listSnake[sign]->head->data.direc!=UP && listSnake[sign]->head->data.direc!= DOWN)){  //判断玩家是否将蛇头方向置为向上
            if((listSnake[sign]->head->data.direc==LEFT || listSnake[sign]->head->data.direc==RIGHT) && listSnake[sign]->head->next->data.direc==UP)
            {

            }else{
                pthread_rwlock_wrlock(&snakeRwlock);
                listSnake[sign]->head->data.direc = DOWN;
                pthread_rwlock_unlock(&snakeRwlock);
                printf("用户:%d 向下\n",sign);
            }
        }else if(clienData.direc==LEFT && (listSnake[sign]->head->data.direc!=LEFT && listSnake[sign]->head->data.direc!= RIGHT)){  //判断玩家是否将蛇头方向置为向上
            if((listSnake[sign]->head->data.direc==UP || listSnake[sign]->head->data.direc==DOWN) && listSnake[sign]->head->next->data.direc==RIGHT)
            {

            }else{
                pthread_rwlock_wrlock(&snakeRwlock);
                listSnake[sign]->head->data.direc = LEFT;
                pthread_rwlock_unlock(&snakeRwlock);
                printf("用户:%d 向左\n",sign);
            }
        }else if(clienData.direc==RIGHT && (listSnake[sign]->head->data.direc!=LEFT && listSnake[sign]->head->data.direc!= RIGHT)){  //判断玩家是否将蛇头方向置为向上
            if((listSnake[sign]->head->data.direc==UP || listSnake[sign]->head->data.direc==DOWN) && listSnake[sign]->head->next->data.direc==LEFT)
            {

            }else{
                pthread_rwlock_wrlock(&snakeRwlock);
                listSnake[sign]->head->data.direc = RIGHT;
                pthread_rwlock_unlock(&snakeRwlock);
                printf("用户:%d 向右\n",sign);
            }
            
        }
    }


}

void empty_fun(int arg)
{
    exitgame = 1;
}

/**
 * @brief 向客户端发送数据线程
 * 
 */
void* client_data_send(void* arg)
{
    //蛇链表数据下标
    signal(SIGPIPE, empty_fun);//这一段代码有什么作用？SIGPIPE是什么意思？empty_fun又有什么含义？？？？
    while (1)
    {
        //等待地图生成 //map是从哪里来的？？？在哪里定义的？？？
        if(map==NULL)
        {
            printf("map is NULL\n");
            continue;
        }
        //等待游戏开始
        if(gameStart==0)
        {
            continue;
        }
        if(listAddrHead->head->next==NULL)//这个判断是什么意思啊？？？
        {
            continue;
        }
        Node_t* nowClient = listAddrHead->head->next;//这个操作是什么意思？？listAddrHead是什么意思？
        
        while(1)//循环发送给各用户画面数据, 直到nowClient->next==NULL
        {
            if(listAddrHead->head->next==NULL || nowClient==NULL)
            {
                break;
            }
            int sign = nowClient->data.sign;
            if(listSnake[sign]==NULL) //如果该用户游戏结束则蛇身链表为空
            {
                break;
            }

            //初始化数据发送结构体
            tcp_server_data send_data;//tcp_server_data的结构体定义是怎样的？？？
            bzero(&send_data,sizeof(tcp_server_data));
            //处理游戏画面数据
            XPRINTF("%s 1\n",__FUNCTION__);
            //上蛇链表读锁	//为什么要进行锁操作呢？？？
            pthread_rwlock_rdlock(&snakeRwlock);
            client_map_proccess(sign,send_data.map_arr);//client_map_proccess()在哪里定义的？？？
            send_data.score = listSnake[sign]->score;
            send_data.mvp = mvp;
            XPRINTF("%s 2\n",__FUNCTION__);
            //解蛇链表读锁
            pthread_rwlock_unlock(&snakeRwlock);
            
            XPRINTF("sign:%d\n",sign);
            send(listSnake[sign]->socketFd,&send_data,sizeof(tcp_server_data),0);//核心的代码，之前的工作都是为了这一步
            XPRINTF("%s 3\n",__FUNCTION__);


            usleep(snake_speed/((listAddrHead->nodeNumber-1)*3));//这个是什么操作？？？
            XPRINTF("%s 6\n",__FUNCTION__);
            
            if(nowClient->next==NULL)
            {
                break;
            }
            XPRINTF("%s 4\n",__FUNCTION__);
            nowClient = nowClient->next;
            XPRINTF("%s 5\n",__FUNCTION__);
            //printf("send end\n");
        }       
    }
}

/*
*@func:创建一个tcp和一个udp的套接字，绑定地址信息为（SERVER_PORT和htonl(INADDR_ANY)）
*@ret: 应该返回一个tcp_socket_fd
*/
static int tcp_socket_init()
{
    //建立一个socket通信 IPV4 TCP
    int socketFd = socket(AF_INET,SOCK_STREAM,0);
    printf("TCP服务器 Port:%d PID:%d \n",SERVER_PORT,getpid());

    //TCP socket 端口复用
	int optval = 1;
	setsockopt(socketFd,SOL_SOCKET,SO_REUSEADDR,&optval, sizeof(optval));
    //setsockopt(udp_socketFd,SOL_SOCKET,SO_REUSEADDR,&optval, sizeof(optval));

    //初始化tcp服务器地址参数
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);//SERVER_PORT是什么，怎么定义的？？？
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    //绑定TCP的socket文件描述符（这里不需要绑定udp的socket吗？？？？？？）
    bind(socketFd,(struct sockaddr*)&server_addr,sizeof(struct sockaddr_in));
    
    //设置TCP socket最多监听数量为4
    listen(socketFd,4);
}

static int udp_socket_init()
{
    //建立一个socket通信 IPV4 UDP
    udp_socketFd = socket(AF_INET,SOCK_DGRAM,0);//为什么是UDP通信呢？？SOCK_DGRAM是什么意思？？？
    printf("UDP服务器 Port:%d PID:%d \n",SERVER_PORT,getpid());

    struct sockaddr_in udp_server_addr;
    udp_server_addr.sin_family = AF_INET;
    udp_server_addr.sin_port = htons(SERVER_PORT);//SERVER_PORT是什么，怎么定义的？？？
    udp_server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    bind(udp_socketFd,(struct sockaddr*)&udp_server_addr,sizeof(struct sockaddr_in));
}

static void create_aux_threads()
{
    //创建TCP发送线程，用于发送游戏画面数据
    pthread_t send_msg_thread; 
    pthread_create(&send_msg_thread,NULL,client_data_send,NULL);//client_data_send线程在哪里定义？？？在上面定义的
    pthread_detach(send_msg_thread);
    //创建蛇自行走线程，用与各蛇运动
    pthread_t thread_snake_run;
    pthread_create(&thread_snake_run,NULL,snake_run,NULL);//thread_snake_run线程在哪里定义的？？？
    pthread_detach(thread_snake_run);
}

/*
*@func:接受新客户端，然后将该客户插入到TCP数据链表
*@ret:返回newClientFd？
*/
int lfd_reactor(int socketFd， int sign)
{
    
    //初始化连接用户地址数据
    struct sockaddr_in child_addr;
    int child_size = sizeof(struct sockaddr_in);
    int newClientFd = accept(socketFd,(struct sockaddr*)&child_addr,&child_size); //与该用户建立通信 ，这个newClientFd是新的client的socketfd

    ElemType_t sockData;
    sockData.addr = child_addr; 
    sockData.socketFd = newClientFd;
    sockData.sign = sign;
    //将该用户数据插入TCP数据链表
    insert_nodeToList_tail(listAddrHead,sockData);
    
    printf("用户IP:%s port:%d\n",inet_ntoa(child_addr.sin_addr),ntohs(child_addr.sin_port));
}

int find_proper_sign()
{
    int sign = 5;
    for (int i = 0; i < 4; i++)
    {
        if(room[i]==1)
        {
            room[i] = 0;
            sign = i;//sign是什么意思？？
            break;
        }
    }
    if(sign==5)//这个判断有什么用？？？
    {
        continue;
    }
    return sign;
}

/*
*@func:创建一个新的蛇，并进行初始化（起始坐标、方向等等）
*@desc: 与全局变量listSnake有关
*/
void init_a_snake(int sign, int newClientFd)
{
    //为新用户建立蛇链表
    listSnake[sign] = create_list();
    //插入蛇身数据
    ElemType_t inputData;
    inputData.x = (sign%2)*40+20;
    inputData.y = (sign/2)*40+20;
    inputData.direc = UP;
    for (int i=0;i<5;i++)
    {
        insert_nodeToList_tail(listSnake[sign],inputData);
        inputData.y++;
    }//这里应该是初始化一段蛇身，蛇的长度为5，初始化的坐标为(20+40k, 20)，方向为向上
    
    //初始化蛇身链表数据 //对蛇的数据结构listSnake[sign]缺乏了解
    listSnake[sign]->socketFd = newClientFd;
    listSnake[sign]->score = 0;
    listSnake[sign]->head->data.start_game = 1;
    listSnake[sign]->head->data.addr = upd_client_addr[sign];
}

/*
*@func:
*@desc:与全局变量listAddrHead有关
*/
int renew_maxid(void)
{
    //遍历TCP数据链表，获取文件描述符最大值
    Node_t* nowNode = listAddrHead->head;
    int maxId = socketFd;
    do
    {
        if(maxId<(nowNode->data.socketFd))
        {
            maxId = nowNode->data.socketFd;//更新maxId
        }
        if(nowNode->next==NULL)
        {
            break;
        }
        nowNode = nowNode->next;//这个是什么意思？？？
    }while(nowNode!=NULL);
    
    return maxId;
}




void close_a_snake()
{
    //上蛇链表写锁
    pthread_rwlock_wrlock(&snakeRwlock);
    XPRINTF("%s 5\n",__FUNCTION__);
    if(listSnake[nowNode->data.sign]!=NULL)
    {
        listSnake[nowNode->data.sign]->head->data.start_game = 0;
    }
    
    reductMap(listSnake[nowNode->data.sign]);
    
    //清除文件集合中该文件描述符
    FD_CLR(nowNode->data.socketFd,&allset);
    
    //关闭该文件描述符
    close(nowNode->data.socketFd);

    //销毁该蛇链表
    room[nowNode->data.sign] = 1;
    if(listSnake[nowNode->data.socketFd]!=NULL)
    {
        
        destroy_list(listSnake[nowNode->data.sign]);
        listSnake[nowNode->data.sign] = NULL;
    }
    
    //在TCP数据链表中删除该用户
    XPRINTF("%s 9\n",__FUNCTION__);
    nowNode->data.sign = 5;
    delete_nodeToList(listAddrHead,nowNode->data);

    XPRINTF("%s 10\n",__FUNCTION__);
    //还原用户离开游戏标志
    exitgame = -1;
    //解蛇链表锁
    pthread_rwlock_unlock(&snakeRwlock);

    printf("有玩家游戏结束\n");
    break;
}


void cfd_reactor()
{
    Node_t* nowNode = listAddrHead->head->next;
    while (1)//遍历各用户文件描述符
    {
        tcp_client_data clienData; 
        bzero(&clienData,sizeof(tcp_client_data));
        XPRINTF("%s 4\n",__FUNCTION__);
        //判断是否有用户文件描述符状态发生改变
        if(FD_ISSET(nowNode->data.socketFd,&rset))//select编程方式需要自己一个一个判断，epoll编程方式不需要
        {
            //尝试读取客户端数据，保存在clienData，并判断是否有用户游戏结束
            if(read(nowNode->data.socketFd,&clienData,sizeof(tcp_client_data))==0)
            {
                close_a_snake();//客户端退出，清理其相关资源
            }
            else//如果该用户游戏没断开
            {
                client_data_recv(nowNode,clienData);//接收数据
            }
        }
        
        if(nowNode->next==NULL)
        {
            break;
        }
        nowNode = nowNode->next;
    }
}



/**
 * @brief TCP服务线程
 *
 */
void* tcp_server(void* arg)
{
    int socketFd = tcp_socket_init();
    int udp_socketFd = udp_socket_init();
    
    //初始化文件集合
    fd_set rset,allset; //fd_set, FD_ZERO这些函数都是在哪里定义的？？？？有什么作用？是什么int、有什么含义???
    //清空文件集合
    FD_ZERO(&allset);
    //将文件套接字描述符加入集合
    FD_SET(socketFd,&allset);

    listAddrHead = create_list();//创建TCP套接字链表
    ElemType_t sockData;
    sockData.socketFd = socketFd; //每个结点都存储一个用户的socket文件描述符  //如何保证每个节点都不一样呢？？？？
    insert_nodeToList_tail(listAddrHead,sockData); //链表首结点是本机socket文件描述符
    int maxId = socketFd;   //记录文件描述符最大值

    create_aux_threads();
    
    int room[4] = {1,1,1,1};
    while (1)
    {
        //初始化文件集合
        rset = allset;
        //多路复用，监听多个套接字
        int nready = select(maxId+1,&rset,NULL,NULL,NULL); //集合中只剩下状态发生变化的套接字
        if(nready < 0)
        {
            perror("select error");
            break;
        }
        
        if(FD_ISSET(socketFd,&rset))//如果监听的套接字状态发生变化，说明有用户已连接上来
        {
            int sign = find_proper_sign();
            int newClientFd = lfd_reactor(socketFd, sign);
            
            FD_SET(newClientFd,&allset);//将新用户文件描述符加入到文件集合
            init_a_snake(sign, newClientFd);
            maxId = renew_maxid();
        }
        else if(listAddrHead->head->next!=NULL) //查看是否有用户登陆（//接收各用户数据）
        {
            //如果有用户登陆
            cfd_reactor();
        }
    }
    printf("主线程退出\n");
    close(socketFd);
}